#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include "communications.h"


void send_announce(int port, char *dossier_donnees, char *dossier_config, int taille_piece, FILE * out)
{
  struct file *file_fichiers = liste_fichier(dossier_donnees, taille_piece);
  struct fichier_partage *fichier = file_fichiers->tete;
  fprintf(out,"announce listen %i seed [",port);
  while (fichier != NULL)
    {
      // Si le fichier n'est pas en cours de télechargement
      fprintf(out,"%s %li %li %s",fichier->nom_fichier,fichier->taille_fichier,fichier->taille_piece,fichier->cle_fichier);
      if (fichier->next != NULL)
        fprintf(out," ");
      fichier = fichier->next;
    }
  fprintf(out,"] leech [");
  file_fichiers=lire_file_dl(dossier_config);
  fichier = file_fichiers->tete;
  while (fichier != NULL)
    {
      // Si le fichier est en cours de télechargement
      fprintf(out,"%s",fichier->cle_fichier);
      if (fichier->next != NULL)
        fprintf(out," ");
      fichier = fichier->next;
    } 
  fprintf(out,"]");
  fflush(out);
  return;
}


void send_look(char **criteres,FILE * out)
{
  fprintf(out,"look [");
  for(int i=0;criteres[i]!=NULL;i++)
    {
      fprintf(out,"%s",criteres[i]);
      if(criteres[i+1]!=NULL)
	{
	  fprintf(out," ");
	}
    }
  fprintf(out,"]");
  return;
}

void send_getfile(char *cle,FILE * out)
{
  fprintf(out,"getfile %s",cle);
  fflush(out);
  return;
}

void send_interested(char *key,FILE*out)
{
  fprintf(out,"interested %s",key);
  fflush(out);
  return;
}

void reply_have(struct fichier_partage* fichier,FILE* out)
{
  fprintf(out,"have %s ",fichier->cle_fichier);
  fwrite(fichier->buffermap,sizeof(char),fichier->taille_buffermap,out);
  fflush(out); 
  return;
}

void send_getpieces(char * key, int * tab_piece,FILE * out)
{
  fprintf(out,"getpieces %s [",key);
  for(int i=0;tab_piece[i]!=-1;i++){
    fprintf(out,"%i",tab_piece[i]);
    if(tab_piece[i+1]!=-1){
      fprintf(out," ");
    }
  }
  fprintf(out,"]");
  fflush(out);
  return;
}

void reply_data(struct fichier_partage *fichier,int* tab_piece,FILE * out,char * dossier)//tab_piece doit finir par -1 !!!
{
  fprintf(out,"data %s [",fichier->cle_fichier);
  for(int i=0;tab_piece[i]!=-1;i++)
    {
      fprintf(out,"%d:",tab_piece[i]);
      send_pieces(fichier,tab_piece[i],out,dossier);
      if(tab_piece[i+1]!=-1){
	fprintf(out," ");
      }
    }
  fprintf(out,"]");
  fflush(out);
  return;
}

void send_update(char *dossier_donnees, char *dossier_config, int taille_fichier, FILE * out)
{
  struct file *file_fichiers = liste_fichier(dossier_donnees, taille_fichier);
  struct fichier_partage *fichier = file_fichiers->tete;
  fprintf(out,"update seed [");
  while (fichier != NULL)
    {
      // Si le fichier n'est pas en cours de télechargement
      fprintf(out,"%s",fichier->cle_fichier);
      if (fichier->next != NULL)
        fprintf(out," ");
      fichier = fichier->next;
    }
  fprintf(out,"] leech [");
  file_fichiers=lire_file_dl(dossier_config);
  fichier = file_fichiers->tete;
  while (fichier != NULL)
    {
      // Si le fichier est en cours de télechargement
      fprintf(out,"%s",fichier->cle_fichier);
      if (fichier->next != NULL)
        fprintf(out," ");
      fichier = fichier->next;
    } 
  fprintf(out,"]");
  fflush(out);
  return;
}


void send_pieces(struct fichier_partage * fichier,int piece_nb,FILE* out,char * dossier){
  FILE * in;
  //On ouvre le fichier
  char * tampon=malloc(sizeof(char)*((strlen(fichier->nom_fichier)+strlen(dossier)+3)));
  tampon=strcpy(tampon,dossier);
  tampon=strcat(tampon,"/");
  tampon=strcat(tampon,fichier->nom_fichier);
  in = fopen(tampon,"r");
  if(in == NULL)
    {
      perror("ERROR open");
      return;
    }
  free(tampon);
  //On place le curseur du fichier
  long position=fichier->taille_piece*(piece_nb);
  fseek(in,position,SEEK_SET);
  //On verifie si ce n'est pas la dernière piece
  int taille_piece_reel=fichier->taille_piece;
  if((fichier->taille_fichier%fichier->taille_piece)!=0 && (piece_nb)==(fichier->taille_fichier/fichier->taille_piece)){
    taille_piece_reel=(fichier->taille_fichier)%(fichier->taille_piece);
  }
  //On lit finalement la piece et on la retourne
  char * piece=malloc(sizeof(char)*taille_piece_reel);
  fread(piece,sizeof(char),taille_piece_reel,in);
  fwrite(piece,sizeof(char),taille_piece_reel,out);
  fclose(in);
  return;
}

//Test pour vérifier le parcours des dossiers et la récupération de la taille des fichiers.
void get_pieces(char* piece,int piece_nb,struct fichier_partage * fichier,char * dossier_dl){
  FILE* out=NULL;
  //On ouvre le fichier si il existe
  char * tampon=malloc(sizeof(char)*(strlen(fichier->nom_fichier)+strlen(dossier_dl)+2));
  tampon=strcpy(tampon,dossier_dl);
  tampon=strcat(tampon,"/");
  tampon=strcat(tampon,fichier->nom_fichier);
  out = fopen(tampon,"r+");
  //Si il n'existe pas on créer le fichier de la bonne taille
  if(out==NULL){
    out=fopen(tampon,"a");
    int nb_pieces = fichier->taille_fichier/fichier->taille_piece;
    char* randmem;
    for(int i=0;i<nb_pieces;i++){
      randmem=malloc(sizeof(char)*fichier->taille_piece);
      fwrite(randmem,sizeof(char),fichier->taille_piece,out);
      free(randmem);   
    }
    if (fichier->taille_fichier%fichier->taille_piece != 0){
      randmem=malloc(sizeof(char)*(fichier->taille_fichier%fichier->taille_piece));
      fwrite(randmem,sizeof(char),fichier->taille_fichier%fichier->taille_piece,out);
      free(randmem);
    }
    fclose(out);
    out=fopen(tampon,"r+");
  }
  free(tampon);
  //On place le curseur du fichier
  long position=fichier->taille_piece*(piece_nb);
  fseek(out,position,SEEK_SET);
  //On verifie si ce n'est pas la dernière piece
  int taille_piece_reel=fichier->taille_piece;
  if(fichier->taille_fichier%fichier->taille_piece!=0 && fichier->taille_fichier/fichier->taille_piece==(piece_nb)){
    taille_piece_reel=fichier->taille_fichier%fichier->taille_piece;
  }
  //On écrit finalement la piece
  fwrite(piece,sizeof(char),taille_piece_reel,out);
  fclose(out);
}

int receive_announce(FILE * in){

  char buff[3];
  if (fread(buff,sizeof(char),2,in)<=0){
    return EXIT_FAILURE;
  }
  buff[2]='\0';
  if(strcmp(buff,"KO")==0){
    return KO;
  }
  if(strcmp(buff,"ok")==0){
    return EXIT_SUCCESS;
  }
  return EXIT_FAILURE;
}


char** cut_char(char* message){	
  char** token=malloc(sizeof(char*)*MAX_TOKEN);
  
  //On decoupe le message
  token[0]=strtok(message," ");
  for(int i=1;i<MAX_TOKEN-1 && token[i-1]!=NULL;i++){
    token[i]=strtok(NULL," ");
  }
  //On test si on a eu la place de decouper tout le message
  if(strtok(NULL," ")!=NULL){
    return NULL;
  }
  return token;
}

int read_white(char* out,FILE*in){
  int i;
  while((i=fread(out,sizeof(char),1,in)>0) && (out[0]==' '));
  if(i==0){
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}

int receive_look(struct file* resultat,FILE*in,int taille_piece){
  char buff[SIZE_BUF];
  char tempc[1];
  char ** token;
  //On lit le debut du message
  if(read_white(buff,in)==EXIT_FAILURE){
    return EXIT_FAILURE;
  }
  if(fread(buff+sizeof(char),sizeof(char),3,in)<=0){   
    return EXIT_FAILURE;
  }
  buff[4]='\0';
  if(strcmp(buff,"list")!=0){   
    return EXIT_FAILURE;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    return EXIT_FAILURE;
  }
  if(buff[0]!='['){
    return EXIT_FAILURE;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    return EXIT_FAILURE;
  } 
  //Puis on lit l'intérieur du message
  buff[1]='\0';
  if(buff[0]!=']'){
    tempc[0]=buff[0];
    buff[0]='\0';
    do{
      strncat(buff,tempc,1);
      if(fread(tempc,sizeof(char),1,in)<=0){
       	return EXIT_FAILURE;
      }
    }while(tempc[0]!=']');
  }
  //Si le tracker n'a rien trouvé
  else{
    return EXIT_SUCCESS;
  }
  //On découpe le corps du message
  token=cut_char(buff);  
  //On rempli notre résultat
  unsigned long taille;
  unsigned char *buffermap;
  int taille_buffermap;
  for(int i=0;token[i]!=NULL;i+=4){
    for(int j=0;j<4;j++){
      if(token[i+j]==NULL){
	fflush(in);   
	return EXIT_FAILURE;
      }
    }
    //Le nom est dans token[i]
    //taille du fichier
    taille=0;
    taille+=atol(token[i+1]);
    if(taille==0){
      fflush(in);   
      return EXIT_FAILURE;
    }
    //test de la cle
    if(strlen(token[i+3])!=32){
      fflush(in);   
      return EXIT_FAILURE;
    }
    // calcul de la taille du buffermap
    int nb_pieces;
    nb_pieces=taille/taille_piece;
    if (taille%taille_piece != 0)
      nb_pieces++;
    taille_buffermap=nb_pieces/8;
    if(nb_pieces%8!=0)
      taille_buffermap++;
    //creation du buffermap vide
    buffermap=malloc(sizeof(char)*nb_pieces);
    for(int j=0;j<taille_buffermap;j++){
      buffermap[j]=0x00;
    }
    enfiler(token[i],taille,taille_piece,token[i+3],buffermap,taille_buffermap,resultat);
    free(buffermap);
  }   
  return EXIT_SUCCESS;
}

char* receive_have(struct fichier_partage * fichier,FILE*in){
  char*buffermap=NULL;
  char buff[SIZE_BUF];
  char tempc[1];
  //On lit le debut du message
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);
    return NULL;
  }
  if(fread(buff+sizeof(char),sizeof(char),3,in)<=0){
    fflush(in);   
    return NULL;
  }
  buff[4]='\0';
  if(strcmp(buff,"have")!=0){
    fflush(in);   
    return NULL;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return NULL;
  }
  tempc[0]=buff[0];
  buff[0]='\0';
  do{
    strncat(buff,tempc,1);
    if(fread(tempc,sizeof(char),1,in)<=0){
      fflush(in);   
      return NULL;
    }
  }while(tempc[0]!=' ');
  if(strcmp(buff,fichier->cle_fichier)!=0){
    fflush(in);   
    return NULL;
  }
  buffermap=malloc(sizeof(char)*fichier->taille_buffermap);
  if(fread(buffermap,sizeof(char),fichier->taille_buffermap,in)<fichier->taille_buffermap){
    fflush(in);
    return NULL;
  }
  fflush(in);   
  return buffermap;
}

//have 8905e92afeb80fc7722ec89eb0bf0966 %buffermap%


char ** receive_getfile(struct fichier_partage * fichier,FILE*in){
  char buff[SIZE_BUF];
  char tempc[1];
  char ** token;
  //On lit le debut du message
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);
    return NULL;
  }
  if(fread(buff+sizeof(char),sizeof(char),4,in)<=0){
    fflush(in);   
    return NULL;
  }
  buff[5]='\0';
  if(strcmp(buff,"peers")!=0){
    fflush(in);   
    return NULL;
  }
  //On trouve et compare la cle
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return NULL;
  }
  tempc[0]=buff[0];
  buff[0]='\0';
  do{
    strncat(buff,tempc,1);
    if(fread(tempc,sizeof(char),1,in)<=0){
      fflush(in);   
      return NULL;
    }
  }while(tempc[0]!=' ');
  if(strcmp(buff,fichier->cle_fichier)!=0){
    fflush(in);   
    return NULL;
  }
  //On récupere le '['
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return NULL;
  }
  if(buff[0]!='['){
    fflush(in);  
    return NULL;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return NULL;
  } 
  //Puis on lit l'intérieur du message
  buff[1]='\0';
  if(buff[0]!=']'){
    tempc[0]=buff[0];
    buff[0]='\0';
    do{
      strncat(buff,tempc,1);
      if(fread(tempc,sizeof(char),1,in)<=0){
	fflush(in);   
	return NULL;
      }
    }while(tempc[0]!=']');
    //On découpe le corps du message
    token=cut_char(buff);
    return token;  
  }
  return NULL;
}


int receive_data(struct fichier_partage * fichier,FILE*in,char* dossier_dl){
  char buff[SIZE_BUF];
  char tempc[1];
  //On lit le debut du message
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);
    return EXIT_FAILURE;
  }
  if(fread(buff+sizeof(char),sizeof(char),3,in)<=0){
    fflush(in);   
    return EXIT_FAILURE;
  }
  buff[4]='\0';
  if(strcmp(buff,"data")!=0){
    fflush(in);   
    return EXIT_FAILURE;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return EXIT_FAILURE;
  }
  tempc[0]=buff[0];
  buff[0]='\0';
  do{
    strncat(buff,tempc,1);
    if(fread(tempc,sizeof(char),1,in)<=0){
      fflush(in);   
      return EXIT_FAILURE;
    }
  }while(tempc[0]!=' ');
  if(strcmp(buff,fichier->cle_fichier)!=0){
    fflush(in);   
    return EXIT_FAILURE;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return EXIT_FAILURE;
  }
  if(buff[0]!='['){
    fflush(in);
    return EXIT_FAILURE;
  }
  if(read_white(buff,in)==EXIT_FAILURE){
    fflush(in);   
    return EXIT_FAILURE;
  }
  while(buff[0]!=']'){
    tempc[0]=buff[0];
    buff[0]='\0';
    do{
      strncat(buff,tempc,1);
      if(fread(tempc,sizeof(char),1,in)<=0){
	fflush(in);   
	return EXIT_FAILURE;
      }
    }while(tempc[0]!=':');
    int piece_nb=0;
    piece_nb+=atoi(buff);
    if(piece_nb<-1){
      fflush(in);
      return EXIT_FAILURE;
    }
    int taille_piece_reel=fichier->taille_piece;
    if(fichier->taille_fichier%fichier->taille_piece!=0 && fichier->taille_fichier/fichier->taille_piece==(piece_nb)){
      taille_piece_reel=fichier->taille_fichier%fichier->taille_piece;
    }
    if(fread(buff,sizeof(char),taille_piece_reel,in)!=taille_piece_reel){
      fflush(in);
      return EXIT_FAILURE;
    }
    get_pieces(buff,piece_nb,fichier,dossier_dl);
    if(read_white(buff,in)==EXIT_FAILURE){
      fflush(in);   
      return EXIT_FAILURE;
    }
  }
  return EXIT_SUCCESS;
}

char * receive_interested(FILE*in)
{
  char * buff=malloc(sizeof(char) * 33);
  char tempc[1];
  //On lit le debut du message
  if(fread(buff,sizeof(char),9,in)<=0)
    {
      return NULL;
    }
  buff[9]='\0';
  if(strcmp(buff,"nterested")!=0)
    { 
      return NULL;
    }
  if(read_white(buff,in)==EXIT_FAILURE)
    {
      return NULL;
    }
  tempc[0]=buff[0];
  buff[0]='\0';
  for(int i = 0;i < 31;i++)
    {
      strncat(buff,tempc,1);
      if(fread(tempc,sizeof(char),1,in)<=0)
	{
	  return NULL;
	}
    }
  strncat(buff,tempc,1);
  return buff;
}
//interested 8905e92afeb80fc7722ec89eb0bf0966

int * receive_getpieces(struct file * file_fichier,struct fichier_partage ** fichier,FILE * in)
{
  char buff[SIZE_BUF];
  char tempc[1];
  int * returnpieces = malloc(sizeof(int)*SIZE_BUF);
  int i=0;
  //On lit le debut du message
  if(fread(buff,sizeof(char),8,in)<=0)
    {
      return NULL;
    }
  buff[8]='\0';
  if(strcmp(buff,"etpieces")!=0)//etpieces
    {
      return NULL;
    }
  if(read_white(buff,in)==EXIT_FAILURE)
    {
      return NULL;
    }
  tempc[0]=buff[0];
  buff[0]='\0';
  do
    {
      strncat(buff,tempc,1);
      if(fread(tempc,sizeof(char),1,in)<=0)
	{
	  return NULL;
	}
    }
  while(tempc[0]!=' ');
  //On test si la clef correspond à qque chose
  *fichier=file_fichier->tete;
  while((*fichier != NULL) && (strcmp(buff,(*fichier)->cle_fichier) != 0))
    {
      *fichier = (*fichier)->next;
    }
  if(*fichier == NULL)
    {
      return NULL;
    }
  if(read_white(buff,in)==EXIT_FAILURE)
    {
      return NULL;
    }
  if(buff[0]!='['){
    return NULL;
  }
  if(read_white(buff,in)==EXIT_FAILURE)
    {
      return NULL;
    }
  while(buff[0]!=']' && tempc[0]!=']')
    {
      tempc[0]=buff[0];
      buff[0]='\0';
      do
	{ 
	  strncat(buff,tempc,1);
	  if(fread(tempc,sizeof(char),1,in) <= 0)
	    {
	      return NULL;
	    }
	}
      while((tempc[0] != ' ') && (tempc[0] != ']'));
      returnpieces[i]=atoi(buff); 
      i++;
      if(tempc[0] != ']')
	{
	  if(read_white(buff,in)==EXIT_FAILURE)
	    { 
	      return NULL;
	    }
	}
    }
  returnpieces[i]=-1;
  return returnpieces;
}

int readstart(FILE * in)
{
  char tempc[1];
  //On lit le debut du message
  if(read_white(tempc,in)==EXIT_FAILURE)
    {
      return -1;
    }
  if(tempc[0] == 'i')
    {
      return 1;
    }
  if(tempc[0] == 'g')
    {
      return 2;
    }
  return -1; 
}
